package net.neocat.tcx.speed;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;

import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * User: jschek
 * Date: 5/2/13
 * Time: 11:21 PM
 * To change this template use File | Settings | File Templates.
 */
public class RangePowerInputBuilder {
    private final NavigableMap<Double, Double> powerBands = Maps.newTreeMap();
    private int gradeSamples = 1;

    public RangePowerInputBuilder addPowerRange(double minGrade, double maxGrade, double power) {
        Map.Entry<Double, Double> nextPower = powerBands.ceilingEntry(maxGrade);
        if (nextPower != null) {
            powerBands.put(maxGrade, nextPower.getValue());
        }
        powerBands.put(minGrade, power);

        return this;
    }

    public RangePowerInputBuilder useDefaultRanges(double basePower) {
        return this.addPowerRange(Double.NEGATIVE_INFINITY, -3.0, basePower * 0.85)
                .addPowerRange(-3.0, -1.0, basePower * 0.95)
                .addPowerRange(-1.0, 1.0, basePower)
                .addPowerRange(1.0, 3.0, basePower * 1.05)
                .addPowerRange(3.0, 7.0, basePower * 1.1)
                .addPowerRange(7.0, Double.POSITIVE_INFINITY, basePower * 1.15);
    }

    public RangePowerInputBuilder setGradeSamples(int gradeSamples) {
        Preconditions.checkArgument(gradeSamples > 0, "gradeSamples must be 1 or more");
        this.gradeSamples = gradeSamples;
        return this;
    }

    public RangePowerInput create() {
        return new RangePowerInput();
    }

    private class RangePowerInput implements PowerInput {
        private final NavigableMap<Double, Double> powerBands = Maps.newTreeMap(RangePowerInputBuilder.this.powerBands);
        private final double[] samples = new double[gradeSamples];
        private transient int index = 0;

        private double sampleGrade(double grade) {
            samples[index] = grade;
            index = (index + 1) % samples.length;

            double sum = 0.0;
            for (double sample : samples) {
                sum += sample;
            }
            return sum / samples.length;
        }

        @Override
        public double calculatePower(long elapsed, TimeUnit elapsedTimeUnit, double grade) {
            Map.Entry<Double, Double> entry = powerBands.floorEntry(sampleGrade(grade));
            if (entry != null) {
                return entry.getValue();
            } else {
                return 0;
            }
        }
    }
}
